package yi.commons.tools;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.groups.Default;

import java.util.*;
import java.util.function.Consumer;

/**
 * 验证工具类，可以基于JSR303/JSR-349规范和Hibernate的实现对一个Bean的字段做验证
 * @author wangda
 * @date 2020-10-13
 */
public class ValidateUtil {

    private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

    /**
     * 校验一个Bean的属性
     * @param bean 被校验的bean
     * @param consumer 校验失败后的处理方法
     */
    public static <T>void validate(T bean, Consumer<Map<String, StringBuilder>> consumer) {
        Map<String, StringBuilder> errorMap = validateBean(bean);
        if (errorMap.isEmpty()) {
            return;
        }

        if (Objects.nonNull(consumer)) {
            consumer.accept(errorMap);
        }
    }

    /**
     * 校验一个Bean的属性<br/>
     * 如果校验不通过，抛出 CommonException异常，详细的校验信息在异常中。
     * @param bean 被校验的bean
     */
    public static <T>void validate(T bean) {
        validate(bean, (errorMap) -> {
            String msg = bean.getClass().getSimpleName() + "对象参数校验失败：" + getMapString(errorMap);
            throw new IllegalArgumentException(msg);
        });
    }

    /**
     * 将Map的值转为字符串，便于显示
     * @param map
     * @return
     */
    private static String getMapString(Map<String, StringBuilder> map) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry entry: map.entrySet()) {
            sb.append("[").append(entry.getKey()).append(":").append(entry.getValue().toString()).append("]");
        }
        return sb.toString();
    }

    /**
     * 校验一个Bean的属性，将校验结果放置在Map中返回。
     * @param bean 被校验的对象
     * @return 校验结果，如果校验通过，返回空map。
     */
    public static <T>Map<String, StringBuilder> validateBean(T bean) {
        Set<ConstraintViolation<T>> validateResultSet = validator.validate(bean, Default.class);

        if(Objects.isNull(validateResultSet) || validateResultSet.isEmpty() ) {
            return Collections.emptyMap();
        }

        Map<String, StringBuilder> errorMap = new HashMap<>();
        for(ConstraintViolation<T> result : validateResultSet){
            String property = result.getPropertyPath().toString();
            if(errorMap.get(property) != null){
                errorMap.get(property).append("," + result.getMessage());
            }else{
                StringBuilder sb = new StringBuilder();
                sb.append(result.getMessage());
                errorMap.put(property, sb);
            }
        }
        return errorMap;
    }

}
